package com.sf.shiva.oms.biz;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.sf.shiva.oms.annotation.Timer;
import com.sf.shiva.oms.annotation.Timer.TimingUnit;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationTaxbillInfo;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybill;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillAddition;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillAdditionExt;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillAddrCons;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillConsign;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillCustoms;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillFee;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillMark;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillPackage;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillService;
import com.sf.shiva.oms.common.domain.operation.waybill.OperationWaybillSpecialHandler;
import com.sf.shiva.oms.common.operation.waybill.dto.ActionOperation;
import com.sf.shiva.oms.common.operation.waybill.dto.WaybillSource;
import com.sf.shiva.oms.common.operation.waybill.dto.WaybillUpdateSource;
import com.sf.shiva.oms.common.util.ExceptionUtils;
import com.sf.shiva.oms.common.util.JsonUtil;
import com.sf.shiva.oms.common.util.ReflectionUtils;

/**
 * 
 * 验证jar包：用之前的json报文转换成实体；显示包内各类的属性数量
 * 
 * <pre>
 * HISTORY
 * ****************************************************************************
 *  ID    DATE            PERSON            REASON
 *  1     2018年6月6日         LA.01367604           Create
 * ****************************************************************************
 * </pre>
 * 
 * @author LA.01367604
 * @version 1.0
 */
public class OpWaybillJarVerifyBiz {

    private static final String clazzNameFormat = "%1$-30s";
    private static final String numFormat = "|%6d";
    private static final String headerFormat = "|%1$-6s";
    private static final String recFileName = "recVersion.txt";
    private static final String lineSplit = " : ";

    // private static final String clazzCountFormat = "%s | %3d";

    public OpWaybillJarVerifyBiz() {
        this.hisVersionCounts = new HashMap<String, Map<String, String>>();
        this.versionNames = new ArrayList<String>();
    }
    public OpWaybillJarVerifyBiz(String resourceFileDir) {
        this.resourceFileDir= resourceFileDir;
        this.hisVersionCounts = new HashMap<String, Map<String, String>>();
        this.versionNames = new ArrayList<String>();
    }

    private String currentVersion;
    private int showVersionNum = 1;
    private String resourceFileDir;
    private File recFile;
    private Map<String, Map<String, String>> hisVersionCounts;
    private List<String> versionNames;
    private boolean isNewVersion= false;
    
    /**
     * 一次性读取本地文件成字符串
     * 
     * @param fileName
     * @return
     * @author LA.01367604-2018年6月6日
     */
    private String readFile2Str(String fileName) {
        String encoding = "UTF-8";
        File file = new File(fileName);
        Long filelength = file.length();
        byte[] filecontent = new byte[filelength.intValue()];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
            return new String(filecontent, encoding);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            System.err.println("The OS does not support " + encoding);
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 写入历史记录文件
     * 
     * @param recFile
     * @param str
     * @param isAppent true-追加写入
     * @author LA.01367604-2018年6月6日
     */
    private void writeStr2File(File recFile, String str, boolean isAppent) {
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(recFile, isAppent)));
            out.write(str);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 转换文件读取json串的格式
     * 
     * @param str
     * @return
     * @author LA.01367604-2018年6月6日
     */
    private String tranJsonStrFormat(String str) {
        String jsonStr = str.replace("\n", "");
        return jsonStr;
    }

    /**
     * 读取历史记录文件，构建成MAP以便打印显示
     * 
     * @author LA.01367604-2018年6月6日
     */
    private void initMap(int needShowVersionNum) {
        if (StringUtils.isNotBlank(this.resourceFileDir)) {
            this.recFile = new File(this.resourceFileDir + "\\" + recFileName);
            try {
                String hisVersionStr = null;
                if (this.recFile.exists()) {
                    hisVersionStr = readFile2Str(this.resourceFileDir + "\\" + recFileName);
                } else {
                    this.recFile.createNewFile();
                }
                createVersionMap(hisVersionStr, needShowVersionNum);
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }
    }

    /**
     * 构建各版本对比的MAP，并设置最终要显示的版本数
     * 
     * @param fileStr
     * @param needShowVersionNum
     * @author LA.01367604-2018年6月6日
     */
    private void createVersionMap(String fileStr, int needShowVersionNum) {
        if (StringUtils.isNotBlank(fileStr)) {
            String[] lines= fileStr.split("\n");
            int recNum=0;
            for (int i=0;i<lines.length;i++){
                try {
                    String[] splitStr = lines[i].split(lineSplit);
                    String versionName = splitStr[0];
                    Map<String, String> countMap = JsonUtil.jsonToMap(splitStr[1]);
                    if (this.currentVersion.equals(versionName)) {
                        this.isNewVersion = true;
                        recNum=i;
                        this.versionNames.add(versionName);
                        continue;
                    }
                    this.hisVersionCounts.put(versionName, countMap);
                    this.versionNames.add(versionName);
                } catch (Exception e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }
            if (!this.isNewVersion) { // 未在历史文件中出现的版本号，认为是新版本，并默认比所有历史版本都新
                this.versionNames.add(this.currentVersion);
                recNum = lines.length;
            }
            this.showVersionNum += recNum < needShowVersionNum ?recNum : needShowVersionNum;
        } else {
            this.versionNames.add(this.currentVersion);
        }
        Collections.reverse(this.versionNames); // 倒序排列
    }

    // /**
    // * 当前版本号是否小于记录中的版本号，如果小于需要替换记录文件内容
    // *
    // * @param versionName
    // * @return
    // * @author LA.01367604-2018年6月6日
    // */
    // private boolean isPreVersion(String versionName) {
    //
    // }
    /**
     * 打印指定类的成员数量，包括历史版本的数量
     * 
     * @param clazz
     * @author LA.01367604-2018年6月6日
     */
    private <T> void printClassFeild(Class<T> clazz) {
        List<Field> fields = ReflectionUtils.getAllField(clazz);
        String clazzName = clazz.toString().substring(clazz.toString().lastIndexOf('.') + 1, clazz.toString().length());
        Map<String, String> currentCountMap = getHisVersionCountByVersion(this.currentVersion);
        currentCountMap.put(clazzName, String.valueOf(fields.size()));
        printStrLine(clazzName, false);
    }

    /**
     * 打印指定枚举类的常量数量，包括历史版本的数量
     * 
     * @param clazz
     * @author LA.01367604-2018年6月6日
     */
    private <T> void printEnumFeild(Class<T> clazz) {
        T[] fields = clazz.getEnumConstants();
        String clazzName = clazz.toString().substring(clazz.toString().lastIndexOf('.') + 1, clazz.toString().length());
        Map<String, String> currentCountMap = getHisVersionCountByVersion(this.currentVersion);
        currentCountMap.put(clazzName, String.valueOf(fields.length));
        printStrLine(clazzName, false);
    }

    private void printStrLine(String clazzName, boolean isHeaderLine) {
        String printContent = String.format(clazzNameFormat, clazzName);
        int startVersion= this.versionNames.indexOf(this.currentVersion);
        for (int i = 0; i < this.showVersionNum; i++) {
            if (isHeaderLine) {
                printContent += String.format(headerFormat, this.versionNames.get(startVersion+i));
            } else {
                int num = getHisVersionCountByClazz(clazzName, this.versionNames.get(startVersion+i));
                printContent += String.format(numFormat, num);
            }
        }
        System.out.println(printContent);
    }

    /**
     * 打印jar全部类的信息，以及历史版本对比信息
     * 
     * @author LA.01367604-2018年6月6日
     */
    private void printAllClass() {
        System.out.println("=====jar包中各类的属性数量如下=====");
        printStrLine("--jar--oms.ow.dto", true);
        printClassFeild(OperationWaybill.class);
        printClassFeild(OperationWaybillAddrCons.class);
        printClassFeild(OperationWaybillAddition.class);
        printClassFeild(OperationWaybillAdditionExt.class);
        printClassFeild(OperationWaybillService.class);
        printClassFeild(OperationWaybillPackage.class);
        printClassFeild(OperationWaybillConsign.class);
        printClassFeild(OperationWaybillCustoms.class);
        printClassFeild(OperationWaybillFee.class);
        printClassFeild(OperationTaxbillInfo.class);
        printClassFeild(OperationWaybillMark.class);
        printClassFeild(OperationWaybillSpecialHandler.class);

        printClassFeild(ActionOperation.class);
        printEnumFeild(WaybillSource.class);
        printClassFeild(WaybillUpdateSource.class);
    }
    
    /**
     * 验证json报文是否能够转换成jar包里的类
     * 
     * @param fileName
     * @return
     * @author LA.01367604-2018年6月6日
     */
    public boolean verifyJson2Object(String fileName){
        try {
            String msg = readFile2Str(this.resourceFileDir + "\\" + fileName);
            OperationWaybill operationWaybill = JsonUtil.json2Object(tranJsonStrFormat(msg), OperationWaybill.class);
            ActionOperation actionJson = JsonUtil.json2Object(operationWaybill.getActionJson(), ActionOperation.class);
            WaybillUpdateSource updateSource = JsonUtil.json2Object(operationWaybill.getUpdateSource(), WaybillUpdateSource.class);
            return true;
        } catch (Exception e) {
            System.out.println("【警告】读取或转换出错：" + ExceptionUtils.extractStackTrace(e));
            return false;
        }
    }

    /**
     * 对比各个版本的jar包中类的信息
     * 
     * @return
     * @author LA.01367604-2018年6月6日
     */
    @Timer(unit = TimingUnit.SECOND)
    public void compObjectByVersions(String currentVersion, int needShowVersionNum) {
        this.currentVersion = currentVersion;
        initMap(needShowVersionNum);
        printAllClass();
        if (this.isNewVersion){
            Collections.reverse(this.versionNames); // 倒序排列 
            StringBuilder strBuilder = new StringBuilder();
            for (int i=0;i<this.hisVersionCounts.size();i++){
                strBuilder.append(this.versionNames.get(i) + lineSplit);
                strBuilder.append(JsonUtil.object2Json(getHisVersionCountByVersion(this.versionNames.get(i))));
                strBuilder.append("\n");
            }
            writeStr2File(this.recFile, strBuilder.toString(), false);
        } else {
            String recContent = this.currentVersion + lineSplit + JsonUtil.object2Json(getHisVersionCountByVersion(this.currentVersion)) + "\n";
            writeStr2File(this.recFile, recContent, true);
        }
    }

    public String getCurrentVersion() {
        return currentVersion;
    }

    public void setCurrentVersion(String currentVersion) {
        this.currentVersion = currentVersion;
    }

    public int getShowVersionNum() {
        return showVersionNum;
    }

    public void setShowVersionNum(int showVersionNum) {
        this.showVersionNum = showVersionNum;
    }

    public String getResourceFileDir() {
        return resourceFileDir;
    }

    public void setResourceFileDir(String resourceFileDir) {
        this.resourceFileDir = resourceFileDir;
    }

    public Map<String, String> getHisVersionCountByVersion(String versionName) {
        if (null == this.hisVersionCounts.get(versionName)) {
            this.hisVersionCounts.put(versionName, new HashMap<String, String>());
        }
        return this.hisVersionCounts.get(versionName);
    }

    public int getHisVersionCountByClazz(String clazzName, String versionName) {
        return Integer.valueOf(this.hisVersionCounts.get(versionName).get(clazzName));
    }

}
